package com.sxl.main;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author songxulin
 * @date 2023/10/16
 */
public class Paxos {

    public static void main(String[] args) {
        Learner L1 = new Learner(9);
        Learner L2 = new Learner(9);
        Learner L3 = new Learner(9);
        Learner L4 = new Learner(9);
        Learner L5 = new Learner(9);
        Learner L6 = new Learner(9);
        Learner L7 = new Learner(9);
        Learner L8 = new Learner(9);
        Learner L9 = new Learner(9);
        List<Learner> learners = Arrays.asList(L1, L2, L3, L4, L5, L6, L7, L8, L9);
        //  basic paxos
        // 指定多个 acceptor
        Acceptor M1 = new Acceptor(0.0,learners);
        Acceptor M2 = new Acceptor(0.3,learners);
        Acceptor M3 = new Acceptor(0.3,learners);
        Acceptor M4 = new Acceptor(0.4,learners);
        Acceptor M5 = new Acceptor(0.4,learners);
        Acceptor M6 = new Acceptor(0.4,learners);
        Acceptor M7 = new Acceptor(0.4,learners);
        Acceptor M8 = new Acceptor(0.4,learners);
        Acceptor M9 = new Acceptor(0.4,learners);
        List<Acceptor> acceptors = Arrays.asList(M1, M2, M3, M4, M5, M6, M7, M8, M9);
        // 指定多个 proposer
        Proposer P1 = new Proposer(1, "M1", acceptors);
        Proposer P2 = new Proposer(2, "M2", acceptors);
        Proposer P3 = new Proposer(4, "M3", acceptors);
        Proposer P4 = new Proposer(4, "M4", acceptors);
        Proposer P5 = new Proposer(5, "M5", acceptors);
        Proposer P6 = new Proposer(6, "M6", acceptors);
        Proposer P7 = new Proposer(7, "M7", acceptors);
        Proposer P8 = new Proposer(8, "M8", acceptors);
        Proposer P9 = new Proposer(9, "M9", acceptors);
        List<Proposer> proposers = Arrays.asList(P1, P2, P3, P4, P5, P6, P7, P8, P9);
        // 暂时没有 learner
//        new Learner();
        // prepare 阶段
        for (Proposer proposer : proposers) {
            new Thread(() -> {
                while (true){
                    proposer.prepare();
                    double random = Math.random();
                    try {
                        TimeUnit.SECONDS.sleep(Math.round(10*random));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        System.out.println("");
//        new Member(1,)
        // accept 阶段

    }
}

class Member {
    /**
     * 成员编号
     */
    int no;
    Proposer proposer;
    Acceptor acceptor;
    Learner learner;

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Proposer getProposer() {
        return proposer;
    }

    public void setProposer(Proposer proposer) {
        this.proposer = proposer;
    }

    public Acceptor getAcceptor() {
        return acceptor;
    }

    public void setAcceptor(Acceptor acceptor) {
        this.acceptor = acceptor;
    }

    public Learner getLearner() {
        return learner;
    }

    public void setLearner(Learner learner) {
        this.learner = learner;
    }

    public Member(int no, Proposer proposer, Acceptor acceptor, Learner learner) {
        this.no = no;
        this.proposer = proposer;
        this.acceptor = acceptor;
        this.learner = learner;
    }
}

// 提议者（Proposer）角色
class Proposer {
    /**
     * 票数
     */
    private int votes;
    private int proposalNumber;
    private String value;
    private List<Acceptor> acceptors;

    public int getProposalNumber() {
        return proposalNumber;
    }

    public void setProposalNumber(int proposalNumber) {
        this.proposalNumber = proposalNumber;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public List<Acceptor> getAcceptors() {
        return acceptors;
    }

    private void tick() {
        proposalNumber++;
    }

    public void setAcceptors(List<Acceptor> acceptors) {
        this.acceptors = acceptors;
    }

    public Proposer(int proposalNumber, String value, List<Acceptor> acceptors) {
        this.proposalNumber = proposalNumber;
        this.value = value;
        this.acceptors = acceptors;
    }

    public void prepare() {
        votes = 0;
        // 发送准备请求给所有接受者
        for (Acceptor acceptor : acceptors) {
            acceptor.receivePrepare(this);
        }
        //判断是否过半
        if (votes > acceptors.size() / 2) {
            //通过
            System.out.println("选举prepare成功:" + value + ":提案标号:" + proposalNumber);
            accept();
        } else {
            //不通过,提案号增加
            System.out.println("选举prepare失败:" + value + ":提案标号:" + proposalNumber);
            tick();
        }
    }

    public void receivePromise(Acceptor acceptor, String acceptedValue) {
        // 处理接受者的承诺
        votes++;
    }

    public void accept() {
        votes = 0;
        // 发送接受请求给所有接受者
        for (Acceptor acceptor : acceptors) {
            acceptor.receiveAccept(this);
        }
        //判断是否过半
        if (votes > acceptors.size() / 2) {
            //通过
            System.out.println("选举accept成功:" + value + ":提案标号:" + proposalNumber);
        } else {
            //不通过,提案号增加
            System.out.println("选举accept失败:" + value + ":提案标号:" + proposalNumber);
            tick();
        }
    }

    public void learn(String learnedValue) {
        // 学习已经接受的值

    }

    public void receiveAccept(Acceptor acceptor, String acceptedValue) {
        // 处理接受者的接受
        votes++;
    }
}

// 接受者（Acceptor）角色
class Acceptor {
    // 模拟超时
    private double timeOutProbability;
    private int promisedProposalNumber;
    private String acceptedValue;
    private List<Learner> learners;

    public int getPromisedProposalNumber() {
        return promisedProposalNumber;
    }

    public void setPromisedProposalNumber(int promisedProposalNumber) {
        this.promisedProposalNumber = promisedProposalNumber;
    }

    public String getAcceptedValue() {
        return acceptedValue;
    }


    public void setAcceptedValue(String acceptedValue) {
        this.acceptedValue = acceptedValue;
    }

    public Acceptor(double timeOut,List<Learner> learners) {
        this.timeOutProbability = timeOut;
        this.learners = learners;
    }

    public synchronized void receivePrepare(Proposer proposer) {
        // 处理准备请求
        if (proposer.getProposalNumber() > promisedProposalNumber) {
            promisedProposalNumber = proposer.getProposalNumber();
            if (timeSleep()) {
                return;
            }
            proposer.receivePromise(this, acceptedValue);
        }
    }

    public synchronized void receiveAccept(Proposer proposer) {
        // 处理接受请求
        if (proposer.getProposalNumber() >= promisedProposalNumber) {
            acceptedValue = proposer.getValue();
            if (timeSleep()) {
                return;
            }
            proposer.receiveAccept(this, acceptedValue);
            for (Learner learner : learners) {
                learner.learn(promisedProposalNumber,acceptedValue);
            }
        }
    }

    private boolean timeSleep() {
        double random = Math.random();
        if (timeOutProbability > random) {
            return true;
        }
        return false;
    }
}

// 学习者（Learner）角色
// 充当记录员
class Learner {
    /**
     * Acceptor 数量
     */
    private int acceptorNum;
    /**
     * 已经接受的提案
     */
    private Map<Integer, String> acceptedValues;
    /**
     * 待选提案
     */
    private Map<Integer, Integer> quasiProposals;

    public Learner(int acceptorNum) {
        this.acceptorNum = acceptorNum;
        this.acceptedValues = new HashMap<>();
        this.quasiProposals = new HashMap<>();
    }

    /**
     * 学习提案值
     *
     * @param proposalNumber 提案编号
     * @param proposalValue  提案值
     * @author songxulin
     * @date 2023/10/17 17:34
     */
    public void learn(int proposalNumber, String proposalValue) {
        if(acceptedValues.get(proposalNumber)!=null){
            return;
        }
        // 从接受者处学习已接受的值
        Integer count = quasiProposals.get(proposalNumber);
        if (count == null) {
            count = 1;
        }else {
            count++;
        }
        quasiProposals.put(proposalNumber,count);
        if (count > getMajorNum(acceptorNum)) {
            acceptedValues.put(proposalNumber, proposalValue);
            System.out.println("acceptedValues:"+proposalNumber+",proposalValue:"+proposalValue);
        }
    }

    private int getMajorNum(int num) {
        return num / 2;
    }
}


